using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Beaker.Collections;
using Beaker.Injection;
using FluentAssertions;

namespace OpenCube.Specs
{
	public interface IInjectionValidationResult
	{
		bool IsRegistered<T>();
		int RegistrationCount<T>();
		void ShouldHaveNoUnverifiedInterfacesLeft();
	}

	/// <summary>
	/// A stub that can be used as an injection container builder to figure out what has been registered and whatnot.
	/// </summary>
	internal sealed class InjectionContainerBuilderStub
		: IContainerBuilder, IInjectionValidationResult
	{
		private readonly IList<Type> _unregisteredInterfaces;
		private readonly IList<Type> _registeredInterfaces;
		private readonly IList<Type> _verifiedInterfaces;

		public InjectionContainerBuilderStub( IEnumerable<Type> interfaceTypes  )
		{
			Contract.Requires<ArgumentNullException>(interfaceTypes != null);
			_unregisteredInterfaces = interfaceTypes.ToList();

			_registeredInterfaces = new	List<Type>();
			_verifiedInterfaces = new List<Type>();
		}

		private class RegistrationInfo
		{
			public int Count { get; set; }
		}

		private readonly Dictionary<Type, RegistrationInfo> _registrationCounters = new Dictionary<Type, RegistrationInfo>();

		private void CountRegistration<TType>()
		{
			CountRegistration(typeof(TType));
		}

		private void CountRegistration(Type type)
		{
			RegistrationInfo registrationInfo;
			if (!_registrationCounters.TryGetValue(type, out registrationInfo))
			{
				registrationInfo = new RegistrationInfo();
				_registrationCounters[type] = registrationInfo;
			}
			registrationInfo.Count++;

			_unregisteredInterfaces.Remove(type);
			_registeredInterfaces.Add(type);
		}

		public void Register<TConcrete>()
			where TConcrete : class
		{
			CountRegistration<TConcrete>();
		}

		public void Register<TService, TImplementation>()
			where TService : class
			where TImplementation : class, TService
		{
			CountRegistration<TService>();
		}

		public void Register<TService>(Func<TService> instanceCreator)
			where TService : class
		{
			CountRegistration<TService>();
		}

		public void Register(Type concreteType)
		{
			CountRegistration(concreteType);
		}

		public void Register(Type serviceType, Type implementation)
		{
			CountRegistration(serviceType);
		}

		public void Register(Type serviceType, Func<object> instanceCreator)
		{
			CountRegistration(serviceType);
		}

		public void RegisterSingleton<TConcrete>()
			where TConcrete : class
		{
			CountRegistration<TConcrete>();
		}

		public void RegisterSingleton<TService, TImplementation>()
			where TService : class
			where TImplementation : class, TService
		{
			CountRegistration<TService>();
		}

		public void RegisterSingleton<TService>(TService instance) where TService : class
		{
			CountRegistration<TService>();
		}

		public void RegisterSingleton<TService>(Func<TService> instanceCreator) where TService : class
		{
			CountRegistration<TService>();
		}

		public void RegisterSingleton(Type serviceType, Type implementation)
		{
			CountRegistration(serviceType);
		}

		public void RegisterSingleton(Type serviceType, Func<object> instanceCreator)
		{
			CountRegistration(serviceType);
		}

		public void RegisterSingleton(Type serviceType, object instance)
		{
			CountRegistration(serviceType);
		}

		public void RegisterAnother<TService, TImplementation>() where TService : class
		{
			CountRegistration<TService>();
		}

		public void RegisterAnotherSingleton<TService, TImplementation>() where TService : class
		{
			CountRegistration<TService>();
		}

		public void RegisterAnother<TService>(Type implementation) where TService : class
		{
			CountRegistration<TService>();
		}

		public void RegisterAnotherSingleton<TService>(Type implementation) where TService : class
		{
			CountRegistration<TService>();
		}

		public void RegisterDefault<TBase, TSpecific>()
			where TBase : class
			where TSpecific : class, TBase
		{
			CountRegistration<TBase>();
		}

		public object PhysicalContainer
		{
			get { return null; }
		}

		public bool IsRegistered<T>()
		{
			return RegistrationCount<T>() > 0;
		}

		public int RegistrationCount<T>()
		{
			_verifiedInterfaces.Add(typeof(T));

			RegistrationInfo registrationInfo;
			if (_registrationCounters.TryGetValue(typeof(T), out registrationInfo))
				return registrationInfo.Count;
			return 0;
		}

		public void ShouldHaveNoUnverifiedInterfacesLeft()
		{
			var missedInterfaces = _unregisteredInterfaces.Except(_verifiedInterfaces).ToList();
			missedInterfaces.Should().BeEquivalentTo(Type.EmptyTypes, because: "all interfaces should be explicitly verified to be registerd or not");
		}
	}
}